package tbpay

import (
	"bytes"
	"crypto/aes"
	"crypto/cipher"
	"encoding/json"
	"errors"
	"fmt"
	"sort"
)

// MapToSortedJSON converts a map to a JSON string with keys sorted by ASCII order
func MapToSortedJSON(m map[string]interface{}) (string, error) {
	var keys []string
	for key := range m {
		keys = append(keys, key)
	}
	sort.Strings(keys)

	sortedMap := make(map[string]interface{})
	for _, key := range keys {
		sortedMap[key] = m[key]
	}

	jsonBytes, err := json.Marshal(sortedMap)
	if err != nil {
		return "", err
	}

	return string(jsonBytes), nil
}

// PKCS7Padding pads the data to be a multiple of the block size
func PKCS7Padding(data []byte, blockSize int) []byte {
	padding := blockSize - len(data)%blockSize
	padText := bytes.Repeat([]byte{byte(padding)}, padding)
	return append(data, padText...)
}

// ecbEncrypter implements cipher.BlockMode interface for ECB encryption
type ecbEncrypter struct {
	b cipher.Block
}

func newECBEncrypter(b cipher.Block) cipher.BlockMode {
	return &ecbEncrypter{b: b}
}

func (x *ecbEncrypter) BlockSize() int { return x.b.BlockSize() }

func (x *ecbEncrypter) CryptBlocks(dst, src []byte) {
	if len(src)%x.BlockSize() != 0 {
		panic("crypto/cipher: input not full blocks")
	}
	if len(dst) < len(src) {
		panic("crypto/cipher: output smaller than input")
	}
	for len(src) > 0 {
		x.b.Encrypt(dst, src[:x.BlockSize()])
		src = src[x.BlockSize():]
		dst = dst[x.BlockSize():]
	}
}

// ecbDecrypter implements cipher.BlockMode interface for ECB decryption
type ecbDecrypter struct {
	b cipher.Block
}

func newECBDecrypter(b cipher.Block) cipher.BlockMode {
	return &ecbDecrypter{b: b}
}

func (x *ecbDecrypter) BlockSize() int { return x.b.BlockSize() }

func (x *ecbDecrypter) CryptBlocks(dst, src []byte) {
	if len(src)%x.BlockSize() != 0 {
		panic("crypto/cipher: input not full blocks")
	}
	if len(dst) < len(src) {
		panic("crypto/cipher: output smaller than input")
	}
	for len(src) > 0 {
		x.b.Decrypt(dst, src[:x.BlockSize()])
		src = src[x.BlockSize():]
		dst = dst[x.BlockSize():]
	}
}

// EncryptAES encrypts the given plaintext using AES/ECB/PKCS7Padding
/**
1.AES加密规则：AES/ECB/PKCS7Padding
2.所有参数加密前需要按照ASCII进行顺序排序（A-Z)
3.排序后按照JSON格式的字符串再进行加密
*/
func EncryptAES(plaintext, key []byte) ([]byte, error) {
	if len(key) != 16 && len(key) != 24 && len(key) != 32 {
		return nil, errors.New("key length must be 16, 24, or 32 bytes")
	}

	block, err := aes.NewCipher(key)
	if err != nil {
		return nil, err
	}

	plaintext = PKCS7Padding(plaintext, block.BlockSize())
	ciphertext := make([]byte, len(plaintext))

	mode := newECBEncrypter(block)
	mode.CryptBlocks(ciphertext, plaintext)

	return ciphertext, nil
}

// 2.所有参数加密前需要按照ASCII进行顺序排序（A-Z)
// 3.排序后按照JSON格式的字符串再进行加密
func sortParamsJson(params map[string]interface{}) string {
	var (
		res  string
		keys []string
	)
	for key := range params {
		if key == "sign" || params[key] == "" || key == "signType" {
			continue
		}

		keys = append(keys, key)
	}
	sort.Strings(keys)
	res = "{"
	for i, key := range keys {
		res = fmt.Sprintf("%s\"%s\":\"%v\"", res, key, params[key])
		if i < len(keys)-1 {
			res = fmt.Sprintf("%s,", res)
		}
	}
	res += "}"

	return res
}
